package com.school.sports.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.school.sports.entity.Backup;
import com.school.sports.mapper.BackupMapper;
import com.school.sports.service.BackupService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 备份服务实现类
 * 处理数据库备份和恢复相关的业务逻辑
 */
@Service
public class BackupServiceImpl extends ServiceImpl<BackupMapper, Backup> implements BackupService {

    private static final Logger logger = LoggerFactory.getLogger(BackupServiceImpl.class);

    // 数据库配置信息
    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String dbUsername;

    @Value("${spring.datasource.password}")
    private String dbPassword;

    // 备份文件存储路径
    @Value("${backup.file.path:D:/school-sports-backups}")
    private String backupBasePath;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Backup performBackup(String backupName, String backupType, Long operatorId) {
        logger.info("开始执行数据库备份，备份名称：{}，备份类型：{}，操作人：{}", backupName, backupType, operatorId);

        try {
            // 创建备份目录
            Path backupDir = Paths.get(backupBasePath);
            if (!Files.exists(backupDir)) {
                Files.createDirectories(backupDir);
            }

            // 生成备份文件名
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
            String fileName = String.format("%s_%s_%s.sql", backupName, backupType, timestamp);
            String filePath = Paths.get(backupBasePath, fileName).toString();

            // 执行数据库备份
            boolean success = executeDatabaseBackup(filePath);

            if (!success) {
                throw new RuntimeException("数据库备份失败");
            }

            // 保存备份记录
            Backup backup = new Backup();
            backup.setBackupName(backupName);
            backup.setBackupType(backupType);
            backup.setFilePath(filePath);
            backup.setBackupAt(LocalDateTime.now());
            backup.setOperatorId(operatorId);

            boolean saved = this.save(backup);
            if (!saved) {
                // 如果保存记录失败，删除备份文件
                Files.deleteIfExists(Paths.get(filePath));
                throw new RuntimeException("保存备份记录失败");
            }

            logger.info("数据库备份成功，备份ID：{}，文件路径：{}", backup.getId(), filePath);
            return backup;

        } catch (Exception e) {
            logger.error("数据库备份失败：{}", e.getMessage(), e);
            throw new RuntimeException("备份失败：" + e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> restoreFromBackup(MultipartFile backupFile, Long operatorId) {
        logger.info("开始从上传文件恢复数据库，文件名：{}，操作人：{}", backupFile.getOriginalFilename(), operatorId);

        Map<String, Object> result = new HashMap<>();

        try {
            // 验证文件类型
            if (!backupFile.getOriginalFilename().toLowerCase().endsWith(".sql")) {
                throw new IllegalArgumentException("只支持.sql格式的备份文件");
            }

            // 保存上传的备份文件
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
            String fileName = "restore_" + timestamp + "_" + backupFile.getOriginalFilename();
            String filePath = Paths.get(backupBasePath, fileName).toString();

            backupFile.transferTo(new File(filePath));

            // 执行数据库恢复
            boolean success = executeDatabaseRestore(filePath);

            if (!success) {
                throw new RuntimeException("数据库恢复失败");
            }

            // 记录恢复操作（可以创建一个恢复记录表）
            logger.info("数据库恢复成功，操作人：{}", operatorId);

            result.put("success", true);
            result.put("message", "数据库恢复成功");
            result.put("restoreTime", LocalDateTime.now());
            result.put("fileName", backupFile.getOriginalFilename());

            return result;

        } catch (Exception e) {
            logger.error("数据库恢复失败：{}", e.getMessage(), e);
            result.put("success", false);
            result.put("message", "恢复失败：" + e.getMessage());
            return result;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> restoreFromBackupRecord(Long backupId, Long operatorId) {
        logger.info("开始从备份记录恢复数据库，备份ID：{}，操作人：{}", backupId, operatorId);

        Map<String, Object> result = new HashMap<>();

        try {
            // 查询备份记录
            Backup backup = this.getById(backupId);
            if (backup == null) {
                throw new IllegalArgumentException("备份记录不存在");
            }

            // 验证备份文件是否存在
            if (!checkBackupFileExists(backupId)) {
                throw new IllegalArgumentException("备份文件不存在或已损坏");
            }

            // 执行数据库恢复
            boolean success = executeDatabaseRestore(backup.getFilePath());

            if (!success) {
                throw new RuntimeException("数据库恢复失败");
            }

            logger.info("数据库恢复成功，备份ID：{}，操作人：{}", backupId, operatorId);

            result.put("success", true);
            result.put("message", "数据库恢复成功");
            result.put("restoreTime", LocalDateTime.now());
            result.put("backupName", backup.getBackupName());
            result.put("backupTime", backup.getBackupAt());

            return result;

        } catch (Exception e) {
            logger.error("数据库恢复失败：{}", e.getMessage(), e);
            result.put("success", false);
            result.put("message", "恢复失败：" + e.getMessage());
            return result;
        }
    }

    @Override
    public IPage<Backup> getBackupPage(Integer currentPage, Integer pageSize,
                                      String backupName, String backupType, Long operatorId,
                                      LocalDateTime startTime, LocalDateTime endTime) {
        Page<Backup> page = new Page<>(currentPage, pageSize);
        return baseMapper.selectBackupPage(page, backupName, backupType, operatorId, startTime, endTime);
    }

    @Override
    public List<Backup> getBackupsByOperator(Long operatorId) {
        return baseMapper.selectByOperatorId(operatorId);
    }

    @Override
    public List<Backup> getBackupsByType(String backupType) {
        return baseMapper.selectByBackupType(backupType);
    }

    @Override
    public List<Backup> getRecentBackups(Integer limit) {
        return baseMapper.selectRecentBackups(limit != null ? limit : 10);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteBackup(Long backupId) {
        try {
            Backup backup = this.getById(backupId);
            if (backup == null) {
                logger.warn("备份记录不存在，ID：{}", backupId);
                return false;
            }

            // 删除备份文件
            Path filePath = Paths.get(backup.getFilePath());
            if (Files.exists(filePath)) {
                Files.delete(filePath);
                logger.info("删除备份文件：{}", backup.getFilePath());
            }

            // 删除数据库记录
            boolean deleted = this.removeById(backupId);
            if (deleted) {
                logger.info("删除备份记录成功，ID：{}", backupId);
            }

            return deleted;

        } catch (Exception e) {
            logger.error("删除备份失败，ID：{}，错误：{}", backupId, e.getMessage(), e);
            throw new RuntimeException("删除备份失败：" + e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteBackups(List<Long> backupIds) {
        if (backupIds == null || backupIds.isEmpty()) {
            return false;
        }

        try {
            boolean allSuccess = true;
            for (Long backupId : backupIds) {
                boolean success = deleteBackup(backupId);
                if (!success) {
                    allSuccess = false;
                    logger.warn("删除备份失败，ID：{}", backupId);
                }
            }

            logger.info("批量删除备份完成，成功：{}，总数：{}",
                backupIds.stream().mapToLong(id -> 1).sum(), backupIds.size());

            return allSuccess;

        } catch (Exception e) {
            logger.error("批量删除备份失败：{}", e.getMessage(), e);
            throw new RuntimeException("批量删除备份失败：" + e.getMessage(), e);
        }
    }

    @Override
    public String downloadBackupFile(Long backupId) {
        Backup backup = this.getById(backupId);
        if (backup == null) {
            throw new IllegalArgumentException("备份记录不存在");
        }

        if (!checkBackupFileExists(backupId)) {
            throw new IllegalArgumentException("备份文件不存在");
        }

        return backup.getFilePath();
    }

    @Override
    public Map<String, Object> validateBackupFile(Long backupId) {
        Map<String, Object> result = new HashMap<>();

        try {
            Backup backup = this.getById(backupId);
            if (backup == null) {
                result.put("valid", false);
                result.put("message", "备份记录不存在");
                return result;
            }

            Path filePath = Paths.get(backup.getFilePath());
            if (!Files.exists(filePath)) {
                result.put("valid", false);
                result.put("message", "备份文件不存在");
                return result;
            }

            // 检查文件大小
            long fileSize = Files.size(filePath);
            if (fileSize == 0) {
                result.put("valid", false);
                result.put("message", "备份文件为空");
                return result;
            }

            // 简单检查文件内容（验证是否为有效的SQL文件）
            try (BufferedReader reader = Files.newBufferedReader(filePath)) {
                String firstLine = reader.readLine();
                if (firstLine == null || !firstLine.contains("-- MySQL dump")) {
                    result.put("valid", false);
                    result.put("message", "备份文件格式无效");
                    return result;
                }
            }

            result.put("valid", true);
            result.put("message", "备份文件有效");
            result.put("fileSize", fileSize);
            result.put("fileSizeMB", fileSize / (1024.0 * 1024.0));

        } catch (Exception e) {
            logger.error("验证备份文件失败，ID：{}，错误：{}", backupId, e.getMessage(), e);
            result.put("valid", false);
            result.put("message", "验证失败：" + e.getMessage());
        }

        return result;
    }

    @Override
    public Map<String, Object> getBackupStatistics() {
        Map<String, Object> statistics = new HashMap<>();

        try {
            // 总备份数量
            long totalBackups = this.count();
            statistics.put("totalBackups", totalBackups);

            // 全量备份数量
            QueryWrapper<Backup> fullBackupWrapper = new QueryWrapper<>();
            fullBackupWrapper.eq("backup_type", "全量");
            long fullBackups = this.count(fullBackupWrapper);
            statistics.put("fullBackups", fullBackups);

            // 增量备份数量
            QueryWrapper<Backup> incrementalBackupWrapper = new QueryWrapper<>();
            incrementalBackupWrapper.eq("backup_type", "增量");
            long incrementalBackups = this.count(incrementalBackupWrapper);
            statistics.put("incrementalBackups", incrementalBackups);

            // 最近7天备份数量
            LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
            QueryWrapper<Backup> recentWrapper = new QueryWrapper<>();
            recentWrapper.ge("backup_at", sevenDaysAgo);
            long recentBackups = this.count(recentWrapper);
            statistics.put("recentBackups", recentBackups);

            // 备份文件总大小
            List<Backup> allBackups = this.list();
            long totalSize = 0;
            int validFiles = 0;

            for (Backup backup : allBackups) {
                try {
                    if (checkBackupFileExists(backup.getId())) {
                        totalSize += getBackupFileSize(backup.getId());
                        validFiles++;
                    }
                } catch (Exception e) {
                    logger.warn("获取备份文件大小失败，ID：{}", backup.getId());
                }
            }

            statistics.put("totalSize", totalSize);
            statistics.put("totalSizeMB", totalSize / (1024.0 * 1024.0));
            statistics.put("validFiles", validFiles);
            statistics.put("invalidFiles", totalBackups - validFiles);

        } catch (Exception e) {
            logger.error("获取备份统计信息失败：{}", e.getMessage(), e);
            statistics.put("error", "获取统计信息失败：" + e.getMessage());
        }

        return statistics;
    }

    @Override
    public Map<String, Object> createScheduledBackup(String backupName, String backupType,
                                                   String scheduleExpression, Long operatorId) {
        Map<String, Object> result = new HashMap<>();

        // 注意：这里需要集成任务调度框架（如Quartz或Spring Task）
        // 为了简化，这里只是记录日志，实际项目中需要实现定时任务

        logger.info("创建定时备份任务，备份名称：{}，类型：{}，调度表达式：{}，操作人：{}",
            backupName, backupType, scheduleExpression, operatorId);

        result.put("success", true);
        result.put("message", "定时备份任务创建成功（功能待实现）");
        result.put("backupName", backupName);
        result.put("backupType", backupType);
        result.put("scheduleExpression", scheduleExpression);

        return result;
    }

    @Override
    public boolean checkBackupFileExists(Long backupId) {
        try {
            Backup backup = this.getById(backupId);
            if (backup == null) {
                return false;
            }

            Path filePath = Paths.get(backup.getFilePath());
            return Files.exists(filePath) && Files.isRegularFile(filePath);

        } catch (Exception e) {
            logger.error("检查备份文件是否存在失败，ID：{}，错误：{}", backupId, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public Long getBackupFileSize(Long backupId) {
        try {
            Backup backup = this.getById(backupId);
            if (backup == null) {
                throw new IllegalArgumentException("备份记录不存在");
            }

            Path filePath = Paths.get(backup.getFilePath());
            if (!Files.exists(filePath)) {
                throw new IllegalArgumentException("备份文件不存在");
            }

            return Files.size(filePath);

        } catch (Exception e) {
            logger.error("获取备份文件大小失败，ID：{}，错误：{}", backupId, e.getMessage(), e);
            return 0L;
        }
    }

    // ========== 私有辅助方法 ==========

    /**
     * 执行数据库备份
     * @param filePath 备份文件路径
     * @return 是否成功
     */
    private boolean executeDatabaseBackup(String filePath) {
        try {
            // 解析数据库连接信息
            String dbName = extractDatabaseName(dbUrl);
            String host = extractHost(dbUrl);
            int port = extractPort(dbUrl);

            // 构建mysqldump命令
            String command = String.format(
                "mysqldump -h%s -P%d -u%s -p%s %s > %s",
                host, port, dbUsername, dbPassword, dbName, filePath
            );

            // 执行命令
            Process process = Runtime.getRuntime().exec(command);
            int exitCode = process.waitFor();

            if (exitCode == 0) {
                logger.info("数据库备份成功，文件：{}", filePath);
                return true;
            } else {
                logger.error("数据库备份失败，退出码：{}", exitCode);
                return false;
            }

        } catch (Exception e) {
            logger.error("执行数据库备份失败：{}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 执行数据库恢复
     * @param filePath 备份文件路径
     * @return 是否成功
     */
    private boolean executeDatabaseRestore(String filePath) {
        try {
            // 解析数据库连接信息
            String dbName = extractDatabaseName(dbUrl);
            String host = extractHost(dbUrl);
            int port = extractPort(dbUrl);

            // 构建mysql命令
            String command = String.format(
                "mysql -h%s -P%d -u%s -p%s %s < %s",
                host, port, dbUsername, dbPassword, dbName, filePath
            );

            // 执行命令
            Process process = Runtime.getRuntime().exec(command);
            int exitCode = process.waitFor();

            if (exitCode == 0) {
                logger.info("数据库恢复成功，文件：{}", filePath);
                return true;
            } else {
                logger.error("数据库恢复失败，退出码：{}", exitCode);
                return false;
            }

        } catch (Exception e) {
            logger.error("执行数据库恢复失败：{}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 从JDBC URL中提取数据库名称
     */
    private String extractDatabaseName(String jdbcUrl) {
        // jdbc:mysql://localhost:3306/xyydh
        int lastSlash = jdbcUrl.lastIndexOf('/');
        if (lastSlash != -1) {
            return jdbcUrl.substring(lastSlash + 1);
        }
        throw new IllegalArgumentException("无法从JDBC URL中提取数据库名称：" + jdbcUrl);
    }

    /**
     * 从JDBC URL中提取主机地址
     */
    private String extractHost(String jdbcUrl) {
        // jdbc:mysql://localhost:3306/xyydh
        int start = jdbcUrl.indexOf("://") + 3;
        int end = jdbcUrl.indexOf(':', start);
        if (end == -1) {
            end = jdbcUrl.indexOf('/', start);
        }
        if (end == -1) {
            end = jdbcUrl.length();
        }
        return jdbcUrl.substring(start, end);
    }

    /**
     * 从JDBC URL中提取端口号
     */
    private int extractPort(String jdbcUrl) {
        // jdbc:mysql://localhost:3306/xyydh
        int colonIndex = jdbcUrl.indexOf(':', jdbcUrl.indexOf("://") + 3);
        if (colonIndex != -1) {
            int slashIndex = jdbcUrl.indexOf('/', colonIndex);
            if (slashIndex == -1) {
                slashIndex = jdbcUrl.length();
            }
            String portStr = jdbcUrl.substring(colonIndex + 1, slashIndex);
            try {
                return Integer.parseInt(portStr);
            } catch (NumberFormatException e) {
                return 3306; // 默认MySQL端口
            }
        }
        return 3306; // 默认MySQL端口
    }
}